<?php
session_start();
/**
 * SuSiteAdmin_1_0.php
 * 在线备份网站(数据库和网站文件)、在线部署网站(数据库和网站文件)、在线上传文件、在线数据库管理工具、在线文件编辑器, 不依赖ftp和服务器帐号(单页绿色文件,方便部署)
 * @since 1.0 <2015-11-11> SoChishun <14507247@qq.com> Added.
 */
// 程序版本号 [2015-11-11] Added.
$version = '1.0';
// session键名 [2015-11-11] Added.
define('SESS_LOGIN_KEY', 'sess_susiteadmin');

// 主配置 [2015-11-11] Added.
$config = array(
    /* 用户配置 */
    'users' => array(
        'admin' => array('admin123', array('*')),
    ),
    /* 数据库配置 */
    'db' => array(
        'DB_HOST' => '127.0.0.1', // 服务器地址
        'DB_USER' => 'root', // 用户名
        'DB_PWD' => 'root', // 密码
        'DB_PORT' => 3306, // 端口
        'DB_NAME' => 'db_xwam_v1', // 数据库名
        'DB_CHARSET' => 'utf8', // 字符集
    ),
    /* 备份目录配置 (相对于站点根目录) */
    'backup_dir' => '/Backup/SSA/',
);

// 登录信息 [2015-11-11] Added.
$login_data = isset($_SESSION[SESS_LOGIN_KEY]) ? $_SESSION[SESS_LOGIN_KEY] : false;

$action = I('action');
$view = I('view', 'okbackup');
switch ($action) {
    case 'login': // 用户登录
        if (!SuSiteAdmin::user_login($config, $msg)) {
            redirect('?r=fail', 1, $msg);
        } else {
            redirect('?r=ok');
        }
        break;
    case 'logout': // 注销登录
        SuSiteAdmin::user_logout();
        redirect('?r=ok');
        break;
    case 'del': // 删除路径(文件或目录)
        if (!SuSiteAdmin::act_delete_path($path, $msg)) {
            redirect('?path=' . $path, 1, $msg);
        } else {
            redirect('?path=' . path_getdir($path), 1, '恭喜,操作成功!');
        }
        break;
    case 'zip': // 压缩
        if (!SuSiteAdmin::act_zip($msg)) {
            redirect('?path=' . $path, 1, $msg);
        } else {
            redirect('?path=' . $path, 1, '恭喜,操作成功!');
        }
        break;
    case 'unzip': // 解压缩
        if (!SuSiteAdmin::act_unzip($msg)) {
            redirect('?path=' . $path, 1, $msg);
        } else {
            redirect('?path=' . $path, 1, '恭喜,操作成功!');
        }
        break;
}
?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>SuSiteAdmin-<?php echo $version ?></title>
        <script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
        <style type="text/css">
            body {font-size:12px; color:#333;}
            a{text-decoration: none;}
            textarea{font-size:12px;line-height:18px; padding:5px;}
            th{font-weight: normal;}
            .userbar a:before{content: '['}
            .userbar a:after{content: ']'}
            .dir-contents{width:1050px; display:table;}
            .dir-contents a{ margin-right:20px;line-height:21px;text-decoration:none;float:left;}
            .blue{color:#0000DB}
            .lightblue{color:#1bd1a5}
            .purple{color:#9900ff}
            .green{color:#009900}
            .red{color:#F00}
            .grey {color:#999;}
            .nav { line-height: 18px;}
            .nav a { color:#333;}
            .nav a:before { content: ' » ' }
            .nav a:first-child:before {content: ''}
            .nav div { color:#CCC; border-bottom:solid 1px #CCC; margin-bottom:5px;}
            span.red { margin-left: 10px;}
        </style>
    </head>
    <body>
        <?php if (!$login_data): ?>
            <!-- 用户登录表单 -->
            <form method="post">
                <table>
                    <tr><th>用户名：</th><td><input type="text" name="uname" placeholder="用户名" required="required" /></td></tr>
                    <tr><th>密&nbsp;码：</th><td><input type="password" name="upwd" placeholder="密码" required="required" /></td></tr>
                </table>
                <input type="hidden" name="action" value="login" />
                <button type="submit">登录</button><button type="reset">重置</button>
            </form>
            <!-- /用户登录表单 -->
            <?php exit('<body></html>'); ?>
        <?php endif; ?>
        <!-- 用户信息栏 -->
        <div class="userbar">
            欢迎您, <?php echo $login_data['user_name'] ?> <a href="?action=logout">注销</a>
            <?php
            if (SuSiteAdmin::check_rule('okbackup')) {
                echo '<a href="?view=okbackup">一键备份</a>';
            }
            if (SuSiteAdmin::check_rule('okrecover')) {
                echo '<a href="?view=okrecover">一键还原</a>';
            }
            if (SuSiteAdmin::check_rule('okclear')) {
                echo '<a href="?view=okclear">一键清理</a>';
            }
            ?>
        </div>            
        <!-- /用户信息栏 -->
        <?php if ('okbackup' == $view && SuSiteAdmin::check_rule('okbackup')): ?>
            <!-- 一键备份 -->
            <fieldset>
                <legend><span>[+]</span>一键备份</legend>
                <form method="post">
                    <table>
                        <tr><td class="red">注意：一键备份将会备份数据库和网站所有数据,备份前建议先清理一下数据<a href="?view=okclear">[一键清理]</a></td></tr>
                        <tr><td><button type="submit">立即备份</button></td></tr>
                    </table>
                </form>
                <fieldset>
                    <legend>备份文件</legend>
                    <?php
                    $files = SuSiteAdmin::get_dir_contents($config['backup_dir']);
                    foreach ($files as $file) {
                        echo '<div><a href="#">', $file['name'], '</a> <a href="?action=download&path=' . $file['path'] . '">[下载]</a> <a href="?action=delfile&path=', $file['path'], '">[删除]</a></div>';
                    }
                    ?>
                    <div><a href="#">backup_20151111_112532.zip</a> <a href="#">[下载]</a> <a href="#">[删除]</a></div>
                    <div><a href="#">backup_20151111_112532.zip</a> <a href="#">[下载]</a> <a href="#">[删除]</a></div>
                    <div><a href="#">backup_20151111_112532.zip</a> <a href="#">[下载]</a> <a href="#">[删除]</a></div>
                    <div><a href="#">backup_20151111_112532.zip</a> <a href="#">[下载]</a> <a href="#">[删除]</a></div>
                </fieldset>
            </fieldset>
            <!-- /一键备份 -->
        <?php endif; ?>
        <?php if ('okrecover' == $view && SuSiteAdmin::check_rule('okrecover')): ?>
            <!-- 一键恢复 -->
            <fieldset>
                <legend><span>[+]</span>一键恢复</legend>
                <table>
                    <tr><td class="red">注意：一键恢复将会重新部署网站,如果网站已存在,则旧的资料将被删除</td></tr>
                </table>

                <form>
                    <table><tr><td>上传备份文件：<div class="red">(注意：文件不能超过2MB,如果文件超过2MB,请先用第三方FTP软件上传到/var/www/html/Backup/Recover/目录下,然后刷新本页面)</div></td></tr>
                        <tr><td><input type="file" name="file" /><button type="submit">上传文件</button></td></tr></table>
                </form>
                <form method="post">
                    <table>
                        <tr><td>
                                <div>请勾选要恢复的文件：</div>
                                <div><input type="radio" name="a">backup_20151111_112532.zip <a href="#">[删除]</a></div>
                                <div><input type="radio" name="a">backup_20151111_112532.zip <a href="#">[删除]</a></div>
                                <div><input type="radio" name="a">backup_20151111_112532.zip <a href="#">[删除]</a></div>
                            </td></tr>
                        <tr><td><button type="submit">立即恢复</button></td></tr>
                    </table>
                </form>
            </fieldset>
        </fieldset>
        <!-- /一键恢复 -->
    <?php endif; ?>
    <?php if ('okclear' == $view && SuSiteAdmin::check_rule('okclear')): ?>
        <!-- 一键清理 -->
        <fieldset>
            <legend><span>[+]</span>一键清理</legend>
            <table>
                <tr><td class="red">注意：一键清理将会清理数据库无效数据、网站缓存文件等</td></tr>
            </table>
            <button type="submit">立即清理</button>
        </fieldset>
    </fieldset>
    <!-- /一键清理 -->
<?php endif; ?>
</body>
</html>
<?php
/* * ****************************************************************************************************
  函数 :)
 * **************************************************************************************************** */

/**
 * 获取浏览器参数
 * @param string $name
 * @param mixed $defv
 * @return mixed
 * @since 1.0 <2015-8-13> SoChishun Added.
 */
function I($name, $defv = '') {
    if (isset($_GET[$name])) {
        return $_GET[$name];
    }
    return isset($_POST[$name]) ? $_POST[$name] : $defv;
}

/**
 * URL重定向
 * @param string $url 重定向的URL地址
 * @param integer $time 重定向的等待时间（秒）
 * @param string $msg 重定向前的提示信息
 * @return void
 * @since 1.0 <2015-10-7> from ThinkPHP
 */
function redirect($url, $time = 0, $msg = '') {
//多行URL地址支持
    $url = str_replace(array("\n", "\r"), '', $url);
    if (empty($msg))
        $msg = "系统将在{$time}秒之后自动跳转到{$url}！";
    if (!headers_sent()) {
// redirect
        if (0 === $time) {
            header('Location: ' . $url);
        } else {
            header("refresh:{$time};url={$url}");
            echo($msg);
        }
        exit();
    } else {
        $str = "<meta http-equiv='Refresh' content='{$time};URL={$url}'>";
        if ($time != 0)
            $str .= $msg;
        exit($str);
    }
}

/**
 * 获取文件扩展名类型
 * @param string $exten 扩展名(不带.)
 * @return string
 * @since 1.0 <2015-10-9> SoChishun Added.
 */
function get_exten_catetory($exten) {
    if ($exten) {
        $filetypes = array('zip' => array('zip', 'rar', '7-zip', 'tar', 'gz', 'gzip'), 'doc' => array('txt', 'rtf', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'wps', 'et'), 'script' => array('php', 'js', 'css', 'c'), 'image' => array('jpg', 'jpeg', 'png', 'gif', 'tiff', 'psd', 'bmp', 'ico'));
        foreach ($filetypes as $catetory => $extens) {
            if (in_array($exten, $extens)) {
                return $catetory;
            }
        }
    }
    return '';
}

/**
 * 绝对路径转相对路径
 * @param string $path
 * @return string
 * @since 1.0 <2015-10-9> SoChishun Added.
 */
function path_ator($path) {
    $root = $_SERVER['DOCUMENT_ROOT'];
    $path = substr($path, strlen($root));
    if ('/' != DIRECTORY_SEPARATOR) {
        $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
    }
    return $path;
}

/**
 * 相对路径转绝对路径
 * @param string $path
 * @return string
 * @since 1.0 <2015-10-9> SoChishun Added.
 */
function path_rtoa($path) {
    $root = $_SERVER['DOCUMENT_ROOT'];
    if ('/' != DIRECTORY_SEPARATOR) {
        $path = str_replace('/', DIRECTORY_SEPARATOR, $path);
    }
    return $root . $path;
}

/**
 * 获取文件的目录地址
 * @param string $path
 * @param boolean $is_r 是否相对路径
 * @return string
 * @since 1.0 <2015-10-9> SoChishun Added.
 */
function path_getdir($path, $is_r = true) {
    if (!$path || is_dir($is_r ? path_rtoa($path) : $path)) {
        return $path;
    }
    return pathinfo($path, PATHINFO_DIRNAME);
}

/**
 * 页面主类
 * @since 1.0 <2015-11-11> SoChishun <14507247@qq.com> Added.
 */
class SuSiteAdmin {

    /**
     * 版本号
     * @var string
     * @since 1.0 <2015-11-11> SoChishun Added.
     */
    CONST VERSION = '1.0.0';

    /**
     * 显示网站目录的项目内容
     * @since 1.0 <2015-5-11> SoChishun Added.
     */
    static function index($view, $path) {
// 面包屑导航
        self::location_to_breadcrumb($path);
// 视图显示
        switch ($view) {
            case 'newfile':
                self::view_create_file($path);
                break;
            case 'newdir':
                self::view_create_dir($path);
                break;
            case 'rename':
                self::view_rename_path($path);
                break;
            case 'chmod':
                self::view_chmod_path($path);
                break;
            case 'zip':
                self::view_zip();
                break;
            case 'unzip':
                self::view_unzip();
                break;
            default:
// 列出文件
                $sapath = path_rtoa($path);
                if (is_dir($sapath)) {
                    self::view_content_list($path);
                } else if (is_file($sapath)) {
                    self::view_edit_file($path);
                } else {
                    echo '<strong class="red">文件或目录不存在或已删除!</strong>';
                }
                break;
        }
    }

    /**
     * 用户登录操作
     * @param array $config
     * @param string SESS_LOGIN_KEY
     * @param string $msg 错误消息
     * @return boolean 
     * @since 1.0 <2015-10-9> SoChishun Added.
     */
    static function user_login($config, &$msg = '') {
        if (!$_POST || !isset($_POST['uname'])) {
            $msg = '表单数据无效!';
            return false;
        }
        $uname = $_POST['uname'];
        if (!array_key_exists($uname, $config['users'])) {
            $msg = '用户不存在';
            return false;
        }
        $login_data = $config['users'][$uname];
        if ($login_data[0] != $_POST['upwd']) {
            $msg = '密码错误!';
            return false;
        }
        $_SESSION[SESS_LOGIN_KEY] = array('user_name' => $uname, 'rules' => isset($login_data[1]) ? $login_data[1] : false);
        return true;
    }

    /**
     * 用户登出操作
     * @param string SESS_LOGIN_KEY
     * @return boolean 
     * @since 1.0 <2015-10-9> SoChishun Added.
     */
    static function user_logout() {
        if (isset($_SESSION[SESS_LOGIN_KEY])) {
            unset($_SESSION[SESS_LOGIN_KEY]);
        }
        return true;
    }

    /**
     * 检测权限
     * @param type $name
     * @return boolean
     * @since 1.0 <2015-11-11> SoChishun Added.
     */
    static function check_rule($name) {
        if (!isset($_SESSION[SESS_LOGIN_KEY])) {
            return false;
        }
        $login_data = $_SESSION[SESS_LOGIN_KEY];
        $rules = $login_data['rules'];
        if (is_array($rules) && (in_array('*', $rules) || in_array($name, $rules))) {
            return true;
        }
        return false;
    }

    /**
     * 删除路径(文件或目录)
     * @param string $path 路径
     * @param string $msg 错误消息
     * @return boolean|string
     * @since 1.0 <2015-10-7> SoChishun Added.
     * @since 2.0 <2015-10-8> SoChishun 将delete_file和delete_dir合并到delete_path
     */
    static function act_delete_path($path, &$msg = '') {
        if ('yes' != I('do')) {
// 为防止黑客破坏,删除操作需要手动增加参数do=yes
            $msg = '非法操作!';
            return false;
        }
        if ('/' == $path) {
            $msg = '根目录无法删除!';
            return false;
        }
        $path = path_rtoa($path);
        if (is_file($path)) {
            if (!@unlink($path)) {
                $msg = '文件删除失败!';
                return false;
            }
            return true;
        }
        if (is_dir($path)) {
            if (!@rmdir($path)) {
                $msg = '目录删除失败!(非空目录或权限不足)';
                return false;
            }
            return true;
        }
        $msg = '不是有效文件或目录!';
        return false;
    }

    /**
     * 压缩操作
     * @param string $msg
     * @return boolean
     * @since 1.0 <2015-10-9> SoChishun Added.
     */
    static function act_zip(&$msg = '') {
        $filename = I('filename');
        $content = I('content');
        if (!$filename || !strpos($filename, '.')) {
            $msg = '压缩文件名无效!';
            return false;
        }
        if (!$content) {
            $msg = '压缩内容无效!';
            return false;
        }
        $include = array();
        $exclude = array();
        $paths = explode(PHP_EOL, $content);
        foreach ($paths as $path) {
            if (0 === strpos($path, 'exclude ')) {
                $exclude[] = trim(substr($path, 8));
            } else {
                $include[] = $path;
            }
        }
        if (!$include) {
            $msg = '压缩内容无效!';
            return false;
        }
        $zip = new ZipHelper();
        return $zip->zip($filename, $msg, $include, $exclude, I('rootpath'));
    }

    /**
     * 解压缩文件
     * @param type $msg
     * @return type
     */
    static function act_unzip(&$msg = '') {
        $root = I('root');
        $filename = I('filename');
        if (!$filename) {
            $msg = '压缩文件路径无效!';
            return false;
        }
        if (!$root) {
            $msg = '解压缩路径无效!';
            return false;
        }
        $zip = new ZipHelper();
        return $zip->unzip($filename, $root, $msg);
    }

    /**
     * 返回指定路径下的内容
     * @param string $directory 相对路径
     * @param array $config 选项
     * @return array
     * @throws Exception
     * @since 1.0 <2015-5-11> SoChishun Added.
     * @since 1.1 <2015-10-8> SoChishun 新增filetype文件类别属性
     */
    static function get_dir_contents($directory, $options = array()) {
        $config = array('name' => true, 'path' => true, 'real_path' => true, 'relative_path' => false, 'exten' => false, 'ctime' => false, 'mtime' => false, 'size' => false, 'is_dir' => true, 'is_file' => false, 'is_link' => false, 'is_executable' => false, 'is_readable' => false, 'is_writable' => false, 'filetype' => false);
        if ($options) {
            $config = array_merge($config, $options);
        }
        try {
            $dir = new DirectoryIterator(path_rtoa($directory));
        } catch (Exception $e) {
            exit($directory . ' 不存在或不可读!');
        }
        $files = array();
        foreach ($dir as $file) {
            if ($file->isDot()) {
                continue;
            }
            if ($config['name']) {
                $item['name'] = $file->getFileName();
            }
            if ($config['path']) {
                $item['path'] = $file->getPath();
            }
            if ($config['real_path']) {
                $item['real_path'] = $file->getRealPath();
            }
            if ($config['relative_path']) {
                $item['relative_path'] = path_ator($file->getRealPath());
            }
            $exten = $file->getExtension();
            if ($config['exten']) {
                $item['exten'] = $exten;
            }
            if ($config['filetype']) {
                $item['filetype'] = get_exten_catetory($exten);
            }
            if ($config['mtime']) {
                $item['mtime'] = $file->getMTime();
            }
            if ($config['ctime']) {
                $item['ctime'] = $file->getCTime();
            }
            if ($config['size']) {
                $item['size'] = $file->getSize();
            }
            if ($config['is_dir']) {
                $item['is_dir'] = $file->isDir();
            }
            if ($config['is_file']) {
                $item['is_file'] = $file->isFile();
            }
            if ($config['is_link']) {
                $item['is_link'] = $file->isLink();
            }
            if ($config['is_executable']) {
                $item['is_executable'] = $file->isExecutable();
            }
            if ($config['is_readable']) {
                $item['is_readable'] = $file->isReadable();
            }
            if ($config['is_writable']) {
                $item['is_writable'] = $file->isWritable();
            }
            $files[] = $item;
        }
        return $files;
    }
}

/**
 * 压缩类
 * @since 1.0 <2015-10-9> SoChishun Added.
 */
class ZipHelper {

    /**
     * 解压缩之
     * @param type $filename
     * @param type $root
     * @param type $msg
     * @return boolean
     * @since 1.0 <2015-10-9> SoChishun Added.
     */
    function unzip($filename, $root, &$msg = '') {
        if (!$filename) {
            $msg = '压缩文件名无效!';
            return false;
        }
        $zip = new ZipArchive();
        $msg = $zip->open($filename);
        if (true !== $msg) {
            $msg = var_export($msg, true);
            return false;
        }
        $zip->extractTo($root);
        $zip->close();
        return true;
    }

    /**
     * 压缩之
     * @param type $filename
     * @param type $msg
     * @param type $include
     * @param type $exclude
     * @param string $trimpath
     * @param string $comment
     * @return boolean
     * @since 1.0 <2015-10-9> SoChishun Added.
     */
    function zip($filename, &$msg = '', $include = array(), $exclude = array(), $trimpath = '', $comment = 'default') {
        if (!$filename) {
            $msg = '压缩文件名无效!';
            return false;
        }
        if (!$include) {
            $msg = '压缩内容无效!';
            return false;
        }
        if ('default' == $comment) {
            $comment = basename($filename) . PHP_EOL . 'Generate at ' . date('Y-m-d H:i:s') . PHP_EOL . 'Powerd by SuSiteAdmin.'; // 注释内容
        }
        try {
            $zip = new ZipArchive();
            $msg = $zip->open($filename, ZIPARCHIVE::OVERWRITE);
            if (true !== $msg) {
                $msg = var_export($msg, true);
                return false;
            }
            if ($comment) {
                $zip->setArchiveComment($comment);
            }
            if ($trimpath) {
                $trimpath = rtrim($trimpath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
            }
            $substart = strlen($trimpath);
            foreach ($include as $source) {
                $this->zip_dir($zip, $source, $exclude, $substart);
            }
            $zip->close();
            return true;
        } catch (Exception $ex) {
            $msg = $ex->getMessage();
            return false;
        }
    }

    /**
     * 递归压缩整个目录
     * @param ZipArchive $zip
     * @param string $source 包含的路径
     * @param array $exclude 排除的路径
     * @param int $substart 开始截取的路径字符串(用于去除路径中的根目录路径)
     * @since 1.0 <2015-8-28> SoChishun Added.
     */
    function zip_dir(&$zip, $source, $exclude, $substart = 0) {
        if (is_dir($source)) {
            $source = rtrim($source, DIRECTORY_SEPARATOR);
            if ($handle = opendir($source)) {
                while (false !== ( $f = readdir($handle) )) {
                    if ('.' == $f || '..' == $f) {
                        continue;
                    }
                    $filename = $source . DIRECTORY_SEPARATOR . $f;
                    if (is_dir($filename)) {
                        if ($exclude && in_array($filename, $exclude)) {
                            continue;
                        }
                        $this->zip_dir($zip, $filename, $exclude, $substart);
                    } else {
                        if ($exclude && in_array($filename, $exclude)) {
                            continue;
                        }
                        $zip->addFile($filename, substr($filename, $substart));
                    }
                }
                closedir($handle);
            }
        } else {
            if ($exclude && in_array($source, $exclude)) {
                return;
            }
            $zip->addFile($source);
        }
    }

}
